package cn.yhjz.platform.system.config;

import cn.yhjz.platform.system.config.security.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

/**
 * SpringSecurity 5.4.x以上新用法配置
 */
@Configuration
@Slf4j
@EnableWebSecurity
//securedEnabled启用@Secured注解
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true)
public class SecurityConfig {

    @Autowired
    private CustomizeAuthenticationSuccessHandler customizeAuthenticationSuccessHandler;
    @Autowired
    private CustomizeAuthenticationFailHandler customizeAuthenticationFailHandler;
    @Autowired
    private LogoutHandler logoutHandler;
    @Autowired
    private DeniedHandler deniedHandler;
    @Autowired
    private CuAuthenticationEntryPoint authenticationEntryPoint;
    @Autowired
    private ExpiredStrategy expiredStrategy;
    @Autowired
    private TokenFilter tokenFilter;

    @Bean
    SecurityFilterChain filterChain(HttpSecurity httpSecurity) throws Exception {
        log.info("开始设置spring-security");
        //csrf会拦截所有的post请求
        httpSecurity.csrf().disable();
        httpSecurity.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);
        /*
         *如果要自定义验证逻辑，逻辑写在authenticationProvider中。
         *只要注入了AuthenticationProvider的bean就能实现自定义认证，并不需要这里设置。
         * 本工程的实现是SelfAuthenticationProvider.java
         */
//        httpSecurity.authenticationProvider(authenticationProvider);
//        AuthenticationManagerBuilder authenticationManagerBuilder =
//                httpSecurity.getSharedObject(AuthenticationManagerBuilder.class);
//        authenticationManagerBuilder.eraseCredentials(false);
//        httpSecurity.authenticationManager(authenticationManagerBuilder.build());
        // SS 自带/login和/logout两个路径，也可以自定义路径
        httpSecurity.formLogin().permitAll()
                //认证成功
                .successHandler(customizeAuthenticationSuccessHandler)
                //认证失败
                .failureHandler(customizeAuthenticationFailHandler);
        //登出成功
        httpSecurity.logout().permitAll().logoutSuccessHandler(logoutHandler)
                //删除cookies
                .deleteCookies();
        //无权访问
        httpSecurity.exceptionHandling().accessDeniedHandler(deniedHandler)
                //未登录时的处理逻辑
                .authenticationEntryPoint(authenticationEntryPoint);
        //spring security有一条过滤器链，下面配置此链的过滤规则
        //requestMatchers()配置的是哪些url进行安全控制，authorizeRequests()配置的是如何进行控制
        //配置此chain的过滤规则，此方法的源码注释中有详细的用法
        httpSecurity.requestMatchers(requestMatchers -> {
            /*
            mvcMatchers是用springmvc的@requestMapping相同的匹配语法
            另外还有antMatchers，代表使用ant匹配语法
            例如 /test   和    /test/**    的区别
            */
            requestMatchers.mvcMatchers("/**");
        });
//        httpSecurity.anonymous().antMatchers("/test/**");
//        httpSecurity.requestMatchers().anyRequest();
        //authorize:授权。配置授权的
        httpSecurity.authorizeRequests((authorize) -> {
                    //这里注意：anonymous(匿名的)，如果用户登录了，此规则会被拦截，即此规则只能由未登录的客户端访问
//                    authorize.antMatchers("/test/**").anonymous();
                    //permitAll表示客户端均可访问此规则，不管是否登录
                    //authenticated表示需要登录才能访问
                    authorize.mvcMatchers("/login").permitAll();
                    authorize.mvcMatchers("/login/getCurrentUser").permitAll();
                    authorize.mvcMatchers("/logout").permitAll();
                    authorize.mvcMatchers("/test/**").permitAll();
                    authorize.mvcMatchers("/odx/**").permitAll();
                    //除上面配置的之外，其他的所有请求都要有权限认证
                    authorize.anyRequest().authenticated();
                }
        );
        //http基本权限认证，是http协议自带的。withDefaults返回空函数
//        httpSecurity.httpBasic(withDefaults());
        httpSecurity.sessionManagement()
                //最多同时登录一个账号
                .maximumSessions(1)
                //登录过期或异地登录的处理逻辑
                .expiredSessionStrategy(expiredStrategy);
        httpSecurity.addFilterBefore(tokenFilter, UsernamePasswordAuthenticationFilter.class);
        SecurityFilterChain o = httpSecurity.build();
        return o;
    }

    @Bean
    public BCryptPasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }


    /**
     * 注入AuthenticationManagerBuilder，设置eraseCredentials为false
     * spring security默认登录后会清空密码，这里设置不用清空
     * 把@Autowired去掉，现在不用这个配置了
     * @param builder
     */
//    @Autowired
    public void authManager(AuthenticationManagerBuilder builder) {
        builder.eraseCredentials(false);
        log.info("AuthenticationManagerBuilder");
    }

}
